<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>Hit testing, moving objects</title>
<link rel="stylesheet" href="/cfg/format.css" type="text/css">
<meta http-equiv="content-type" content="text/html; charset=utf-8">
<meta name="keywords" content="Java2D, Java, tutorial, hit testing, moving, 
scaling, resizing objects, programming, Graphics, multiplatform">
<meta name="description" content="In this part of the Java 2D tutorial, we do hit testing and moving objects.">
<meta name="language" content="en">
<meta name="author" content="Jan Bodnar">
<meta name="distribution" content="global">

<script type="text/javascript" src="/lib/jquery.js"></script>
<script type="text/javascript" src="/lib/common.js"></script>

</head>

<body>


<div class="container">

<div id="wide_ad" class="ltow">
<script type="text/javascript"><!--
google_ad_client = "pub-9706709751191532";
/* 160x600, August 2011 */
google_ad_slot = "2484182563";
google_ad_width = 160;
google_ad_height = 600;
//-->
</script>
<script type="text/javascript"
src="http://pagead2.googlesyndication.com/pagead/show_ads.js">
</script>
</div>

<div class="content">


<a href="/" title="Home">Home</a>&nbsp;
<a href="..">Contents</a>


<h1>Hit testing, Moving objects</h1>


<p>
In this part of the Java 2D programming tutorial, we will first talk 
about hit testing. We will show, how to determine, if we have clicked 
inside a shape on a panel. In the second example, we will create two 
shapes, that we can move with a mouse on the panel and resize them 
with a mouse wheel. In the last example, we will be resizing a rectangle 
with two controlling points. 
</p>

<div class="center">
<script type="text/javascript"><!--
google_ad_client = "ca-pub-9706709751191532";
/* top_horizontal */
google_ad_slot = "3327173442";
google_ad_width = 468;
google_ad_height = 60;
//-->
</script>
<script type="text/javascript"
src="http://pagead2.googlesyndication.com/pagead/show_ads.js">
</script>
</div>

<h2>Hit testing</h2>

<p>
Hit testing is determining if we have clicked inside a <code>Shape</code> 
with a mouse pointer. Each Shape has a <code>contains()</code> method. 
The method tests if a specified  <code>Point2D</code> is inside the 
boundary of a <code>Shape</code>.
</p>

<div class="codehead">HitTesting.java</div>
<pre class="code">
package com.zetcode;

import java.awt.AlphaComposite;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;

import java.awt.geom.Ellipse2D;
import java.awt.geom.Rectangle2D;

import javax.swing.JFrame;
import javax.swing.JPanel;


public class HitTesting extends JPanel {

    private Rectangle2D rect;
    private Ellipse2D ellipse;

    private float alpha_rectangle;
    private float alpha_ellipse;


    public HitTesting() {

        this.addMouseListener(new HitTestAdapter());

        rect = new Rectangle2D.Float(20f, 20f, 80f, 50f);
        ellipse = new Ellipse2D.Float(120f, 30f, 60f, 60f);

        alpha_rectangle = 1f;
        alpha_ellipse = 1f;
    }


    public void paint(Graphics g) {
        super.paint(g);

        Graphics2D g2d = (Graphics2D)g;

        g2d.setColor(new Color(50, 50, 50));

        RenderingHints rh =
            new RenderingHints(RenderingHints.KEY_ANTIALIASING, 
                RenderingHints.VALUE_ANTIALIAS_ON);

        rh.put(RenderingHints.KEY_RENDERING,
               RenderingHints.VALUE_RENDER_QUALITY);

        g2d.setRenderingHints(rh);

        g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER,
                                                    alpha_rectangle));
        g2d.fill(rect);

        g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER,
                                                    alpha_ellipse));
        g2d.fill(ellipse);
    }


    public static void main(String[] args) {

        JFrame frame = new JFrame("Hit testing");
        frame.add(new HitTesting());
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setSize(250, 150);
        frame.setLocationRelativeTo(null);
        frame.setVisible(true);
    }


    class RectRunnable implements Runnable {

        private Thread runner;

        public RectRunnable() {
            runner = new Thread(this);
            runner.start();
        }

        public void run() {

            while (alpha_rectangle >= 0) {
                repaint();
                alpha_rectangle += -0.01f;

                if (alpha_rectangle &lt; 0) {
                    alpha_rectangle = 0;
                }

                try {
                    Thread.sleep(50);
                } catch (InterruptedException e) {
                    System.out.println("interrupted");
                }
            }
        }
    }


    class HitTestAdapter extends MouseAdapter implements Runnable {

        private RectRunnable rectAnimator;
        private Thread ellipseAnimator;


        public void mousePressed(MouseEvent e) {
            int x = e.getX();
            int y = e.getY();

            if (rect.contains(x, y)) {

                rectAnimator = new RectRunnable();
            }

            if (ellipse.contains(x, y)) {

                ellipseAnimator = new Thread(this);
                ellipseAnimator.start();
            }
        }

        public void run() {
            while (alpha_ellipse >= 0) {

                repaint();
                alpha_ellipse += -0.01f;

                if (alpha_ellipse &lt; 0) {
                    alpha_ellipse = 0;

                }

                try {
                    Thread.sleep(50);
                } catch (InterruptedException e) {
                    System.out.println("interrupted");
                }
            }
        }
    }
}
</pre>

<p>
In our example, we have two <code>Shapes</code>. 
A rectangle and a circle. By clicking on them they 
gradually begin to fade away. In this example, we work 
with <code>Threads</code>. 
</p>


<pre class="explanation">
private Rectangle2D rect;
private Ellipse2D ellipse;
</pre>

<p>
We work with a rectangle and an ellipse. 
</p>


<pre class="explanation">
private float alpha_rectangle;
private float alpha_ellipse;
</pre>

<p>
These two variables control the transparency of the two geometrical objects. 
</p>

<pre class="explanation">
g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER,
                                            alpha_rectangle));
g2d.fill(rect);
</pre> 

<p>
Inside the <code>paint()</code> method, we set 
the transparency of the rectangle. 
The <code>alpha_rectangle</code> is computed 
inside a dedicated <code>Thread</code>.
</p>

<p>
The <code>HitTestAdapter</code> class is responsible 
for handling of mouse events. It does implement the
<code>Runnable</code> interface, which means that it 
also creates the first thread. 
</p>

<pre class="explanation">
if (ellipse.contains(x, y)) {

    ellipseAnimator = new Thread(this);
    ellipseAnimator.start();
}
</pre> 

<p>
If we press inside the ellipse a new <code>Thread</code> is created. 
The thread calls the <code>run()</code> method. In our case, 
it is the <code>run()</code> method of
the class itself. (<code>HitTestAdapter</code>)
</p>


<pre class="explanation">
if (rect.contains(x, y)) {

    rectAnimator = new RectRunnable();
}
</pre> 

<p>
For the rectangle, we have a separate inner class. 
A <code>RectRunnable</code> class. This class creates 
its own thread in the constructor. 
</p>

<pre class="explanation">
public void run() {
    while (alpha_ellipse >= 0) {

        repaint();
        alpha_ellipse += -0.01f;
        ...
    }
</pre> 

<p>
Note that the <code>run()</code> method is only called once. 
To actually do something, we have to implement a while loop. The while 
loop repaints the panel and decrements the <code>alpha_ellipse</code> 
variable. 
</p>


<img src="/img/gfx/java2d/hittesting.png" alt="Hit testing">
<div class="figure">Figure: Hit testing</div>


<h2>Moving and Scaling</h2>

<p>
In the next section we will learn how to move and scale graphical objects 
with a mouse on the panel. This is a very interesting piece of code. It can 
be used to move and scale charts, diagrams or other various objects in your application. 
</p>

<div class="codehead">MovingScaling.java</div>
<pre class="code">
package com.zetcode;

import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseWheelEvent;
import java.awt.event.MouseWheelListener;
import java.awt.geom.Ellipse2D;
import java.awt.geom.Rectangle2D;

import javax.swing.JFrame;
import javax.swing.JPanel;


public class MovingScaling extends JPanel {

    private ZRectangle zrect;
    private ZEllipse zell;

    public MovingScaling() {

        MovingAdapter ma = new MovingAdapter();

        addMouseMotionListener(ma);
        addMouseListener(ma);
        addMouseWheelListener(new ScaleHandler());

        zrect = new ZRectangle(50, 50, 50, 50);
        zell = new ZEllipse(150, 70, 80, 80);

        setDoubleBuffered(true);
    }

    public void paint(Graphics g) {
        super.paint(g);
        
        Graphics2D g2d = (Graphics2D) g;
        
        Font font = new Font("Serif", Font.BOLD, 40);
        g2d.setFont(font);
        
        g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
                        RenderingHints.VALUE_ANTIALIAS_ON);
        g2d.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING,
                        RenderingHints.VALUE_TEXT_ANTIALIAS_ON);

        g2d.setColor(new Color(0, 0, 200));
        g2d.fill(zrect);
        g2d.setColor(new Color(0, 200, 0));
        g2d.fill(zell);
    }

    class ZEllipse extends Ellipse2D.Float {
        public ZEllipse(float x, float y, float width, float height) {
            setFrame(x, y, width, height);
        }

        public boolean isHit(float x, float y) {
            if (getBounds2D().contains(x, y)) {
                return true;
            } else {
                return false;
            }
        }

        public void addX(float x) {
            this.x += x;
        }

        public void addY(float y) {
            this.y += y;
        }

        public void addWidth(float w) {
            this.width += w;
        }

        public void addHeight(float h) {
            this.height += h;
        }
    }

    class ZRectangle extends Rectangle2D.Float {

        public ZRectangle(float x, float y, float width, float height) {
            setRect(x, y, width, height);
        }

        public boolean isHit(float x, float y) {
            if (getBounds2D().contains(x, y)) {
                return true;
            } else {
                return false;
            }
        }

        public void addX(float x) {
            this.x += x;
        }

        public void addY(float y) {
            this.y += y;
        }

        public void addWidth(float w) {
            this.width += w;
        }

        public void addHeight(float h) {
            this.height += h;
        }
    }

    class MovingAdapter extends MouseAdapter {

        private int x;
        private int y;

        public void mousePressed(MouseEvent e) {
            x = e.getX();
            y = e.getY();
        }

        public void mouseDragged(MouseEvent e) {

            int dx = e.getX() - x;
            int dy = e.getY() - y;

            if (zrect.isHit(x, y)) {
                zrect.addX(dx);
                zrect.addY(dy);
                repaint();
            }

            if (zell.isHit(x, y)) {
                zell.addX(dx);
                zell.addY(dy);
                repaint();
            }

            x += dx;
            y += dy;
        }   
    }


    class ScaleHandler implements MouseWheelListener {
        public void mouseWheelMoved(MouseWheelEvent e) {

            int x = e.getX();
            int y = e.getY();

            if (e.getScrollType() == MouseWheelEvent.WHEEL_UNIT_SCROLL) {

                if (zrect.isHit(x, y)) {
                    float amount =  e.getWheelRotation() * 5f;
                    zrect.addWidth(amount);
                    zrect.addHeight(amount);
                    repaint();
                }

                if (zell.isHit(x, y)) {
                    float amount =  e.getWheelRotation() * 5f;
                    zell.addWidth(amount);
                    zell.addHeight(amount);
                    repaint();
                }
            }
        }
    }

    public static void main(String[] args) {

        JFrame frame = new JFrame("Moving and Scaling");
        frame.add(new MovingScaling());
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setSize(300, 300);
        frame.setLocationRelativeTo(null);
        frame.setVisible(true);
    }
}

</pre>

<p>
In our code example, we have two graphical objects. A rectangle and a circle. 
You can move both by clicking on them and
dragging them. You can also scale them up or down by positioning 
the mouse cursor over the objects and moving the mouse wheel. 
</p>


<pre class="explanation">
private ZRectangle zrect;
private ZEllipse zell;
</pre> 

<p>
As we have already mentioned, we have a rectangle and an ellipse on our panel. 
Both classes extend the functionality of
a built-in classes from Java AWT package. 
</p>

<pre class="explanation">
addMouseMotionListener(ma);
addMouseListener(ma);
addMouseWheelListener(new ScaleHandler());
</pre> 

<p>
We register three listeners. These listeners will capture mouse press, 
mouse drag and mouse wheel events. 
</p>

<pre class="explanation">
class ZEllipse extends Ellipse2D.Float {
    public ZEllipse(float x, float y, float width, float height) {
        setFrame(x, y, width, height);
    }

    public boolean isHit(float x, float y) {
        if (getBounds2D().contains(x, y)) {
            return true;
        } else {
            return false;
        }
    }

...

}
</pre> 

<p>
This code excerpt shows a <code>ZEllipse</code> class. 
It extends the built-in <code>Ellipse2D.Float</code> class. 
It adds functionality for scaling and moving an ellipse. 
For example, the <code>isHit()</code> method determines, 
if the mouse pointer is inside the area of an ellipse.
</p>


<p>
The <code>MovingAdapter</code> class handles the mouse 
press and mouse drag events.  
</p>

<pre class="explanation">
public void mousePressed(MouseEvent e) {
    x = e.getX();
    y = e.getY();
}
</pre> 

<p>
In the <code>mousePressed()</code> method, we remember the 
initial x, y coordinates of the object. 
</p>

<pre class="explanation">
int dx = e.getX() - x;
int dy = e.getY() - y;
</pre> 

<p>
Inside the <code>mouseDragged()</code> method, we calculate 
the distance by which we have dragged the object. 
</p>

<pre class="explanation">
if (zrect.isHit(x, y)) {
    zrect.addX(dx);
    zrect.addY(dy);
    repaint();
}
</pre> 

<p>
Here if we are inside the area of the rectangle, we update the x, y 
coordinates of the rectangle and repaint the panel. 
</p>

<pre class="explanation">
x += dx;
y += dy;
</pre> 

<p>
The initial coordinates are updated.
</p>

<p>
The <code>ScaleHandler</code> class handles the scaling of the objects. 
</p>


<pre class="explanation">
if (e.getScrollType() == MouseWheelEvent.WHEEL_UNIT_SCROLL) {

    if (zrect.isHit(x, y)) {
        float amount =  e.getWheelRotation() * 5f;
        zrect.addWidth(amount);
        zrect.addHeight(amount);
        repaint();
    }

...

}
</pre> 

<p>
Here if we move a mouse wheel and our cursor is inside the area of 
a rectangle, the rectangle is resized and the 
panel repainted. The amount of the scaling is computed from the 
<code>getWheelRotation()</code> method, which returns 
the amount of the wheel rotation. 
</p>


<img src="/img/gfx/java2d/movingscaling.png" alt="Moving and scaling objects">
<div class="figure">Figure: Moving and scaling objects</div>


<h2>Resize Rectangle</h2>

<p>
In the next example, we will show how to resize a shape. 
Our shape will be a rectangle. 
On our rectangle, we will draw two small black rectangles. 
By clicking on these tiny rectangles and dragging them, 
we can resize our "big" rectangle. 
</p>

<div class="codehead">ResizeRectangle.java</div>
<pre class="code">
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;

import javax.swing.JFrame;
import javax.swing.JPanel;


public class ResizeRectangle extends JPanel {

    private Point2D[] points;
    private int SIZE = 8;
    private int pos;


    public ResizeRectangle() {

        addMouseListener(new ShapeTestAdapter());
        addMouseMotionListener(new ShapeTestAdapter());
        pos = -1;

        points = new Point2D[2];
        points[0] = new Point2D.Double(50, 50);
        points[1] = new Point2D.Double(150, 100);
    }

    public void paintComponent(Graphics g) {
        super.paintComponent(g);

        Graphics2D g2 = (Graphics2D) g;

        for (int i = 0; i &lt; points.length; i++) {
            double x = points[i].getX() - SIZE / 2;
            double y = points[i].getY() - SIZE / 2;
            g2.fill(new Rectangle2D.Double(x, y, SIZE, SIZE));
        }

        Rectangle2D s = new Rectangle2D.Double();
        s.setFrameFromDiagonal(points[0], points[1]);

        g2.draw(s);
    }


    private class ShapeTestAdapter extends MouseAdapter {

        public void mousePressed(MouseEvent event) {

            Point p = event.getPoint();

            for (int i = 0; i &lt; points.length; i++) {
                double x = points[i].getX() - SIZE / 2;
                double y = points[i].getY() - SIZE / 2;

                Rectangle2D r = new Rectangle2D.Double(x, y, SIZE, SIZE);

                if (r.contains(p)) {
                    pos = i;
                    return;
                }
            }
        }

        public void mouseReleased(MouseEvent event) {
            pos = -1;
        }

        public void mouseDragged(MouseEvent event) {
            if (pos == -1)
                return;

            points[pos] = event.getPoint();
            repaint();
        }
    }


    public static void main(String[] args) {

        JFrame frame = new JFrame("Resize Rectangle");

        frame.add(new ResizeRectangle());
        frame.setSize(300, 300);
        frame.setLocationRelativeTo(null);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setVisible(true);
    }
}
</pre>

<p>
Basically, there are two ways to create a rectangle. By providing 
x, y coordinates plus the width and height of the
rectangle. Another way is to provide the top-left and bottom-right 
points. In our code example, we will use both
methods. 
</p>

<pre class="explanation">
private Point2D[] points;
</pre> 

<p>
In this array, we will store points, that will make our rectangle. 
</p>

<pre class="explanation">
private int SIZE = 8;
</pre> 

<p>
This is the size of the small black rectangles. 
</p>

<pre class="explanation">
points = new Point2D[2];
points[0] = new Point2D.Double(50, 50);
points[1] = new Point2D.Double(150, 100);
</pre> 

<p>
These are the initial coordinates for a rectangle. 
</p>

<pre class="explanation">
Rectangle2D s = new Rectangle2D.Double();
s.setFrameFromDiagonal(points[0], points[1]);

g2.draw(s);
</pre> 

<p>
Here we draw a rectangle from the points. 
</p>

<pre class="explanation">
for (int i = 0; i &lt; points.length; i++) {
    double x = points[i].getX() - SIZE / 2;
    double y = points[i].getY() - SIZE / 2;
    g2.fill(new Rectangle2D.Double(x, y, SIZE, SIZE));
}
</pre> 

<p>
This code draws the two small controlling rectangles. 
</p>

<pre class="explanation">
public void mousePressed(MouseEvent event) {

    Point p = event.getPoint();

    for (int i = 0; i &lt; points.length; i++) {
        double x = points[i].getX() - SIZE / 2;
        double y = points[i].getY() - SIZE / 2;

        Rectangle2D r = new Rectangle2D.Double(x, y, SIZE, SIZE);

        if (r.contains(p)) {
            pos = i;
            return;
        }
    }
}
</pre> 

<p>
In the <code>mousePressed()</code> method, we determine, 
if we have clicked inside one of the
two controlling points. If we hit one of them, the 
<code>pos</code> variable stores which of them it was. 
</p>

<pre class="explanation">
public void mouseDragged(MouseEvent event) {
    if (pos == -1)
        return;

    points[pos] = event.getPoint();
    repaint();
}
</pre> 

<p>
Here the rectangle is dynamically resized. During the 
<code>mouseDragged()</code> 
event, we get the current point, update our array of 
points and repaint the panel. 
</p>


<img src="/img/gfx/java2d/resizerectangle.png" alt="Resize Rectangle">
<div class="figure">Figure: Resizing a Rectangle</div>


<p>
In this part of the Java 2D tutorial, we covered hit testing and moving objects.
</p>


<div class="center"> 
<script type="text/javascript"><!--
google_ad_client = "pub-9706709751191532";
/* horizontal */
google_ad_slot = "1734478269";
google_ad_width = 468;
google_ad_height = 60;
//-->
</script> 
<script type="text/javascript"
src="http://pagead2.googlesyndication.com/pagead/show_ads.js"> 
</script> 
</div> 
<br>


<div class="botNav, center">
<span class="botNavItem"><a href="/">Home</a></span> ‡ <span class="botNavItem"><a href="..">Contents</a></span> ‡ 
<span class="botNavItem"><a href="#">Top of Page</a></span>
</div>


<div class="footer">
<div class="signature">
<a href="/">ZetCode</a> last modified November 23, 2008  <span class="copyright">&copy; 2007 - 2013 Jan Bodnar</span>
</div>
</div>

</div> <!-- content -->

</div> <!-- container -->

</body>
</html>


